#undef VERBOSE
#ifdef VERBOSE
#define IF_VERBOSE(x) ({x;})
#define IF_N_VERBOSE(x)
#else
#define IF_VERBOSE(x)
#define IF_N_VERBOSE(x) ({x;})
#endif

#undef SIMULATE_POWER_METER  4



#include "SPI.h"
#include <RF24.h>
#include <RF24Network.h>
#include "S_message.h"
#include <printf.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "Battery.h"
#include "sleep.h"

#ifdef SIMULATE_POWER_METER
#include <TimerOne.h>
#endif


//pins for radio
const uint8_t rf_ce = 6;
const uint8_t rf_csn = 5;

//radio channel
const uint8_t radio_channel = 92;

// power supply for the photo transistor
const uint8_t tsl_vdd = 7;

//LED indication
const uint8_t led_indicator = 9;

//DS18B20 pin and temp precision
const uint8_t one_wire = 8;
const uint8_t temp_precision = 9;

//we will force reportinng every time 'cycle' reaches 'report_cycles'
const uint16_t report_cycles = 100; // 1 hour
uint16_t cycle = 0;


volatile boolean waiting;
const uint8_t num_measurements = 1; //number of cycles to measure power consumption and avg over them
unsigned long power;

// Sleep constants.  In this example, the watchdog timer wakes up
// every 4s, and every single wakeup we power up the radio and send
// a reading.  In real use, these numbers which be much higher.
// Try wdt_8s and 7 cycles for one reading per minute.> 1
const wdt_prescalar_e wdt_prescalar = wdt_8s;
const int sleep_cycles_per_transmission = 8;




OneWire oneWire(one_wire);
DallasTemperature tempSensors(&oneWire);
DeviceAddress tempDeviceAddress;



RF24 radio (rf_ce, rf_csn);
RF24Network network (radio);
RF24NetworkHeader header(/*to node*/ 0, /*type*/ 'S'); //header for the send message
S_message message;
S_message refMessage;
const uint16_t my_address = 012;
uint8_t defMask;


bool ok;


void irq(){
  waiting = false;
}

uint16_t calcInstantPower(){
  
  unsigned long s, t;
  const float blinks_per_kwh = 1000;
  const unsigned long timer_timeout = 65000000; // 65 seconds timeout
  unsigned long timer; 
  
  
  attachInterrupt(0, irq, RISING);
  
  //detect first blink and start counting
  timer = micros()+timer_timeout;
  waiting = true;
  while ((s=micros()) < timer && waiting);
  
  //detect second blink and measure the time since first blink
  timer = s+timer_timeout;
  waiting = true;
  while ((t=micros()) < timer && waiting);

  detachInterrupt (0);
  
  t-=s;
 
  //calculate instant power consumptions in kwh
  float p = 3600*1000000 / t / blinks_per_kwh;
  
  //returns the result in wh
  return (uint16_t)(p*1000);
}


#ifdef SIMULATE_POWER_METER
void timerIsr()
{
    // Toggle LED
    static boolean output = HIGH;  digitalWrite(SIMULATE_POWER_METER, output^=1);
}
#endif



void setup(){
  IF_N_VERBOSE (pinMode (0, INPUT_PULLUP));
  IF_N_VERBOSE (pinMode (1, INPUT_PULLUP));
 
  //pinMode (2, INPUT_PULLUP);
  pinMode (3, INPUT_PULLUP);
  pinMode (4, INPUT_PULLUP);
  //pinMode (5, INPUT_PULLUP);
  //pinMode (6, INPUT_PULLUP);
  //pinMode (7, INPUT_PULLUP);
  //pinMode (8, INPUT_PULLUP);
  //pinMode (9, INPUT_PULLUP);
  pinMode (10, INPUT_PULLUP);
  //pinMode (11, INPUT_PULLUP);
  //pinMode (12, INPUT_PULLUP);
  //pinMode (13, INPUT_PULLUP);
 
  ADCSRA &= ~(1<<ADEN); 
  
  
  
  IF_VERBOSE(Serial.begin(57600));
  IF_VERBOSE(Serial.println("Start..."));
  IF_VERBOSE(printf_begin());  
  
#ifdef SIMULATE_POWER_METER
  pinMode(SIMULATE_POWER_METER, OUTPUT);
  Timer1.initialize(1000000); // set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
  Timer1.attachInterrupt( timerIsr ); // attach the service routine here
#endif
  pinMode(tsl_vdd, OUTPUT);
  
  
  radio.begin();
  network.begin (radio_channel, my_address);
  radio.powerDown();
  
  tempSensors.begin();
  
  Sleep.begin(wdt_prescalar,sleep_cycles_per_transmission, led_indicator);
  
  //define active sensors
  defMask = 0;
  bitSet(defMask, S_MASK_POWER);
  bitSet(defMask, S_MASK_BATTERY);
  bitSet(defMask, S_MASK_TEMP);
  
}


void loop(){
  
  cycle++;
  message.mask = 0;
  power = 0;
     
#ifdef SIMULATE_POWER_METER
  Timer1.resume();
#endif   
  
  // Pump the network regularly
  network.update();
  
  //power up the photo transistor
  digitalWrite(tsl_vdd, HIGH);
  delay(2);
  
  //measure power
  if (digitalRead(2) == 1){
    for (uint8_t i=0; i<num_measurements; i++)
      power += calcInstantPower();
    

    message.power_reading = (power / num_measurements);
    if (refMessage.power_reading != message.power_reading)
      bitSet(message.mask, S_MASK_POWER);
  }
  else{
    message.power_reading = 0;
    IF_VERBOSE(printf("No phototransistor attached.\n"));
  }
  
  //measure temperature
  tempSensors.requestTemperatures();
  message.temp_reading = tempSensors.getTempCByIndex(0);
  if (refMessage.temp_reading != message.temp_reading)
    bitSet(message.mask, S_MASK_TEMP);
    
  //reading battery Vcc
  ADCSRA |= (1<<ADEN);
  message.battery_reading = Battery.readVcc();
  if (refMessage.battery_reading != message.battery_reading)
    bitSet(message.mask, S_MASK_BATTERY);
  ADCSRA &= ~(1<<ADEN);
    
  if (cycle>report_cycles){
    message.mask = defMask; //force report
    cycle = 0; //reset timer
  }
  
  
  if (message.mask){
    IF_VERBOSE(printf("%lu:cycle %u, sending %s", millis(), cycle, message.toString()));
    
    ok=network.write(header,&message,sizeof(message));
//    if (!ok){
//      ok =  network.write(header,&message,sizeof(message));
//    }
    
    if (ok) {
      IF_VERBOSE(printf("...OK\n"));
      refMessage = message;
    }
    else{
      IF_VERBOSE(printf("...NOK\n"));
      //as sending measurements during this force_report cycle failed, 
      //make sure next cycle is again force_report
      if (message.mask == defMask) cycle = report_cycles;
    }      
  }
  
  // Be sure to flush the serial first before sleeping, so everything
  // gets printed properly
    
  IF_VERBOSE(Serial.flush(); delay(10);); //enusre enough time to flush..
  


  //Goes to sleep
  radio.powerDown();
  digitalWrite(tsl_vdd, LOW);
  
#ifdef SIMULATE_POWER_METER
  Timer1.stop();
  digitalWrite(SIMULATE_POWER_METER, LOW);
#endif


  
  Sleep.go(!ok);
 
  
  
}

